package org.budo.activiti.config;

import java.util.ArrayList;
import java.util.List;

import javax.sql.DataSource;

import org.activiti.engine.FormService;
import org.activiti.engine.HistoryService;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.delegate.event.ActivitiEventListener;
import org.activiti.engine.form.FormType;
import org.activiti.engine.impl.el.ExpressionManager;
import org.activiti.engine.impl.form.BooleanFormType;
import org.activiti.engine.impl.form.DateFormType;
import org.activiti.engine.impl.form.DoubleFormType;
import org.activiti.engine.impl.form.LongFormType;
import org.activiti.engine.impl.form.StringFormType;
import org.activiti.engine.impl.history.HistoryLevel;
import org.activiti.engine.impl.persistence.entity.TaskEntityManager;
import org.activiti.spring.ProcessEngineFactoryBean;
import org.activiti.spring.SpringProcessEngineConfiguration;
import org.budo.activiti.behavior.BudoActivityBehaviorFactory;
import org.budo.activiti.entity.TaskEntityManagerDelegate;
import org.budo.activiti.event.BudoSpringDispatchActivitiEventListener;
import org.budo.activiti.expression.BudoActivitiExpressionManager;
import org.budo.activiti.form.type.AllowDenyFormType;
import org.budo.activiti.form.type.BooleanFormTypeVertical;
import org.budo.activiti.form.type.BudoEnumFormType;
import org.budo.activiti.form.type.BudoEnumFormTypeVertical;
import org.budo.activiti.form.type.BudoFormTypeList;
import org.budo.activiti.form.type.CheckboxFormType;
import org.budo.activiti.form.type.CheckboxFormTypeVertical;
import org.budo.activiti.form.type.DateFormTypeVertical;
import org.budo.activiti.form.type.DateRangeFormType;
import org.budo.activiti.form.type.DateRangeFormTypeVertical;
import org.budo.activiti.form.type.DateTimeFormType;
import org.budo.activiti.form.type.DateTimeFormTypeVertical;
import org.budo.activiti.form.type.DateTimeRangeFormType;
import org.budo.activiti.form.type.DateTimeRangeFormTypeVertical;
import org.budo.activiti.form.type.DistrictFormType;
import org.budo.activiti.form.type.DistrictFormTypeVertical;
import org.budo.activiti.form.type.DividerFormType;
import org.budo.activiti.form.type.DividerFormTypeVertical;
import org.budo.activiti.form.type.EnumFormTypeVertical;
import org.budo.activiti.form.type.LongFormTypeVertical;
import org.budo.activiti.form.type.MonthFormType;
import org.budo.activiti.form.type.MonthFormTypeVertical;
import org.budo.activiti.form.type.RadioFormType;
import org.budo.activiti.form.type.RadioFormTypeVertical;
import org.budo.activiti.form.type.RateFormType;
import org.budo.activiti.form.type.RateFormTypeVertical;
import org.budo.activiti.form.type.SliderFormType;
import org.budo.activiti.form.type.SliderFormTypeVertical;
import org.budo.activiti.form.type.StringFormTypeVertical;
import org.budo.activiti.form.type.SummernoteFormTypeVertical;
import org.budo.activiti.form.type.SwitchFormType;
import org.budo.activiti.form.type.SwitchFormTypeVertical;
import org.budo.activiti.form.type.TextareaFormTypeVertical;
import org.budo.activiti.form.type.TimeFormType;
import org.budo.activiti.form.type.TimeFormTypeVertical;
import org.budo.activiti.form.type.TimeRangeFormType;
import org.budo.activiti.form.type.TimeRangeFormTypeVertical;
import org.budo.activiti.form.type.UploadFormType;
import org.budo.activiti.form.type.UploadFormTypeVertical;
import org.budo.activiti.form.type.WeekFormType;
import org.budo.activiti.form.type.WeekFormTypeVertical;
import org.budo.activiti.service.api.BudoActivitiService;
import org.budo.activiti.service.impl.BudoActivitiServiceImpl;
import org.budo.support.lang.util.ArrayUtil;
import org.budo.support.spring.bean.factory.util.FactoryBeanUtil;
import org.springframework.context.annotation.Bean;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;

import com.fasterxml.jackson.databind.ObjectMapper;

import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;

/**
 * activiti 配置类 liuzhize 2019年3月7日下午3:26:56
 * 
 * @see org.budo.activiti.service.impl.BudoActivitiServiceImpl
 */
@Getter
@Setter
@Accessors(chain = true)
public class BudoActivitiEngineConfiguration {
    private static final FormType[] DEFAULT_FORM_TYPES = { //
            new StringFormType(), //
            new LongFormType(), //
            new BooleanFormType(), //
            new DateFormType(DateTimeFormType.FORMAT), //
            new DoubleFormType(), //
            //
            new BooleanFormTypeVertical(), //
            new org.budo.activiti.form.type.DateFormType(), //
            new DateFormTypeVertical(), //
            new DateRangeFormType(), //
            new DateRangeFormTypeVertical(), //
            new DateTimeFormType(), //
            new DateTimeFormTypeVertical(), //
            new DateTimeRangeFormType(), //
            new DateTimeRangeFormTypeVertical(), //
            new EnumFormTypeVertical(null), //
            new LongFormTypeVertical(), //
            new StringFormTypeVertical(), //
            new TextareaFormTypeVertical(), //
            new SummernoteFormTypeVertical(), //
            //
            new SliderFormType(), //
            new SliderFormTypeVertical(), //
            //
            new RateFormType(), //
            new RateFormTypeVertical(), //
            //
            new RadioFormType(), //
            new RadioFormTypeVertical(), //
            new CheckboxFormType(), //
            new CheckboxFormTypeVertical(), //
            //
            new SwitchFormType(), //
            new SwitchFormTypeVertical(), //
            //
            new DistrictFormType(), //
            new DistrictFormTypeVertical(), //
            //
            new TimeFormType(), //
            new TimeFormTypeVertical(), //
            new TimeRangeFormType(), //
            new TimeRangeFormTypeVertical(), //
            new UploadFormType(), //
            new UploadFormTypeVertical(), //
            new MonthFormType(), //
            new MonthFormTypeVertical(), //
            new WeekFormType(), //
            new WeekFormTypeVertical(), //
            new AllowDenyFormType(), //
            new DividerFormType(), //
            new DividerFormTypeVertical(), //
            //
            new BudoEnumFormType(null), //
            new BudoEnumFormTypeVertical(null) //
    };

    private DataSource dataSource;

    private List<FormType> formTypes = new ArrayList<FormType>();

    @Bean("budoActivitiService")
    public BudoActivitiService budoActivitiService() {
        return new BudoActivitiServiceImpl() //
                .setDataSource(this.getDataSource());
    }

    @Bean
    public ObjectMapper objectMapper() {
        return new ObjectMapper();
    }

    @Bean
    public ExpressionManager expressionManager() {
        return new BudoActivitiExpressionManager();
    }

    @Bean
    public ProcessEngine processEngine() {
        ProcessEngineFactoryBean processEngineFactoryBean = new ProcessEngineFactoryBean();
        processEngineFactoryBean.setProcessEngineConfiguration(this.processEngineConfiguration());
        return FactoryBeanUtil.getObject(processEngineFactoryBean);
    }

    @Bean
    public PlatformTransactionManager transactionManager() {
        return new DataSourceTransactionManager(this.getDataSource());
    }

    @Bean
    public TaskService taskService() {
        return this.processEngine().getTaskService();
    }

    @Bean
    public FormService formService() {
        return this.processEngine().getFormService();
    }

    @Bean
    public RepositoryService repositoryService() {
        return this.processEngine().getRepositoryService();
    }

    @Bean
    public HistoryService historyService() throws Throwable {
        return this.processEngine().getHistoryService();
    }

    @Bean
    public RuntimeService runtimeService() throws Throwable {
        return this.processEngine().getRuntimeService();
    }

    @Bean
    public TaskEntityManager taskEntityManager() throws Throwable {
        return new TaskEntityManagerDelegate();
    }

    /**
     * 初始化配置
     */
    @Bean
    public SpringProcessEngineConfiguration processEngineConfiguration() {
        SpringProcessEngineConfiguration engineConfiguration = new SpringProcessEngineConfiguration();

        // 执行工作流对应的数据源
        engineConfiguration.setDataSource(this.getDataSource());
        engineConfiguration.setTransactionManager(this.transactionManager());

        engineConfiguration.setAsyncExecutorActivate(false);

        // 流程历史等级
        engineConfiguration.setHistoryLevel(HistoryLevel.FULL);

        // 流程图字体
        engineConfiguration.setActivityFontName("宋体");
        engineConfiguration.setAnnotationFontName("宋体");
        engineConfiguration.setLabelFontName("宋体");

        // 全局事件监听
        if (null == engineConfiguration.getEventListeners()) {
            engineConfiguration.setEventListeners(new ArrayList<ActivitiEventListener>());
        }
        engineConfiguration.getEventListeners().add(new BudoSpringDispatchActivitiEventListener());

        // 表单属性类型
        FormType[] formTypes = ArrayUtil.concat(DEFAULT_FORM_TYPES, this.getFormTypes().toArray(new FormType[this.getFormTypes().size()]));

        engineConfiguration.setFormTypes(new BudoFormTypeList(formTypes));

        engineConfiguration.setActivityBehaviorFactory(new BudoActivityBehaviorFactory());

        engineConfiguration.setExpressionManager(this.expressionManager());

        return engineConfiguration;
    }
}
